home *** CD-ROM | disk | FTP | other *** search
/ PC User 2003 January / Disc 1 / PCU0103CD1.iso / entertn / demos / files / aomtrial.exe / AOM / AI / SCN23P2.XS < prev    next >
Encoding:
Text File  |  2002-09-17  |  32.4 KB  |  973 lines

  1. //==============================================================================
  2. // Scn23p2: AI Scenario Script for scenario 23 player 2
  3. //==============================================================================
  4. //
  5. // NOTES: Base units to maintain are spearmen and slingers.
  6. // At Age 3, slingers are replaced by chariot archers as the base unit.
  7. // Mixes include Camelry, Priests, and Anubites.
  8. //
  9. // Axemen are primarily used as "guard" units on the map, and not sucked into.
  10. /*
  11.                                   *** DIFFICULTY LEVEL NOTES ***
  12.  
  13.    Easy level - Fewer researches.  Fewer attacking units.  Attacks less frequent
  14.     and start later.
  15.  
  16.    Moderate level - Base level.
  17.  
  18.    Difficult - More units on attack and defense, especially sphinxes.
  19.  
  20.    Nightmare - More units on attack and defense, especially sphinxes.
  21. */
  22. //==============================================================================
  23. int difflevel=-1;        
  24.  
  25. //==============================================================================
  26. // Set Town Location
  27. //==============================================================================
  28. void setTownLocation(void)
  29. {
  30.    //Look for the "Town Location" marker.
  31.    kbSetTownLocation(kbGetBlockPosition("3073"));
  32. }
  33.  
  34. //==============================================================================
  35. // miscStartup
  36. //==============================================================================
  37. void miscStartup(void)
  38. {
  39.     difflevel=aiGetWorldDifficulty();
  40.    //Startup message(s).
  41.    aiEcho("");
  42.    aiEcho("");
  43.    aiEcho("Scn06P2 AI Start, filename='"+cFilename+"'.");
  44.    //Spit out the map size.
  45.    aiEcho("  Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+").");
  46.     aiEcho("Difficulty Level="+difflevel+".");
  47.     
  48.     //Cheat like a bastard.  Once only, though.
  49.    kbLookAtAllUnitsOnMap();
  50.    //Calculate some areas.
  51.    kbAreaCalculate(1200.0);
  52.    //Set our town location.
  53.    setTownLocation();
  54.     //Reset random seed
  55.     aiRandSetSeed();
  56.    //Allocate all resources to the root escrow.
  57.    kbEscrowAllocateCurrentResources();
  58. }
  59.  
  60.  
  61. //==============================================================================
  62. //==============================================================================
  63. // Attack stuff.
  64. //==============================================================================
  65. //==============================================================================
  66. //Shared variables.
  67. int numberAttacks=0;
  68. int attackPlayerID=-1;
  69.  
  70. //TODO: Decide how to rep attack group size.
  71. int attackMinimumGroupSize=5;
  72. int attackMaximumGroupSize=8;
  73.  
  74. //Attack 1 vars.
  75. int attackPlan1ID=-1;
  76.  
  77. //Attack 2 vars.
  78. int attackPlan2ID=-1;
  79.  
  80. //Maintain plan IDs.
  81. int maintainPlan1ID=-1;
  82. int maintainPlan2ID=-1;
  83. int maintainPlan3ID=-1;
  84.  
  85. // Route and path vars
  86. int attackRoute1ID=-1;
  87. int attackPath1ID=-1;
  88. int attackRoute2ID=-1;
  89. int attackPath2ID=-1;
  90.  
  91. // Unit Types
  92. int attackerUnitTypeID1=cUnitTypeSpearman;
  93. int attackerUnitTypeID2=cUnitTypeSlinger;
  94. int attackerUnitTypeID3=cUnitTypePriest;
  95. int attackerUnitTypeID4=cUnitTypeSphinx;
  96. int attackerUnitTypeID5=cUnitTypeCamelry;
  97. int attackerUnitTypeID6=cUnitTypeChariotArcher;
  98. int attackerUnitTypeID7=cUnitTypeSiegeTower;
  99.  
  100. bool makingAge3Units=false;
  101. int missileMaintainPlan=-1;
  102.  
  103. //=========================================================================================
  104. // Kidd's cool configQuery function: used to create attack routes, etc.  Oooh, lovin' that!
  105. //=========================================================================================
  106. bool configQuery( int queryID = -1, int unitType = -1, int action = -1, int state = -1, int player = -1, vector center = vector(-1,-1,-1), bool sort = false, float radius = -1 )
  107. {
  108.    if ( queryID == -1)
  109.    {
  110.       return(false);
  111.    }
  112.  
  113.    if (player != -1)
  114.       kbUnitQuerySetPlayerID(queryID, player);
  115.    
  116.    if (unitType != -1)
  117.       kbUnitQuerySetUnitType(queryID, unitType);
  118.  
  119.    if (action != -1)
  120.       kbUnitQuerySetActionType(queryID, action);
  121.  
  122.    if (state != -1)
  123.       kbUnitQuerySetState(queryID, state);
  124.  
  125.    if (center != vector(-1,-1,-1))
  126.    {
  127.       kbUnitQuerySetPosition(queryID, center);
  128.       if (sort == true)
  129.          kbUnitQuerySetAscendingSort(queryID, true);
  130.       if (radius != -1)
  131.          kbUnitQuerySetMaximumDistance(queryID, radius);
  132.    }
  133.    return(true);
  134. }
  135.  
  136. //==============================================================================
  137. // Check for number of AI units near the enemy TC.
  138. // Used to fire off eclipse - should happen fairly early
  139. //==============================================================================
  140. int checkForEclipse(void)
  141. {
  142.     static int eclipseQueryID=-1;
  143.     vector enemyBase=kbGetBlockPosition("3082");
  144.     int unitCount=-1;
  145.  
  146.    if (eclipseQueryID < 0)
  147.    {  
  148.         // Doesn't exist, set it up
  149.       eclipseQueryID = kbUnitQueryCreate("Eclipse Query");
  150.         
  151.       // Get the number
  152.       if ( configQuery( eclipseQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 2, enemyBase, false, 20 ) == false )
  153.          return(-1);
  154.    }
  155.  
  156.    kbUnitQueryResetResults(eclipseQueryID);
  157.    unitCount = kbUnitQueryExecute(eclipseQueryID);
  158.     return(unitCount);
  159. }
  160.  
  161. //==============================================================================
  162. // Check for number of player units near the pyramid.
  163. // Used to fire off Ancestors (if Age 3, duh)
  164. //==============================================================================
  165. int checkForAncestors(void)
  166. {
  167.     static int ancestorsQueryID=-1;
  168.     vector pyramidLocation=kbGetBlockPosition("3073");
  169.     int unitCount=-1;
  170.  
  171.    if (ancestorsQueryID < 0)
  172.    {  
  173.         // Doesn't exist, set it up
  174.       ancestorsQueryID = kbUnitQueryCreate("Ancestors Query");
  175.         
  176.       // Get the number
  177.       if ( configQuery( ancestorsQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 1, pyramidLocation, false, 20 ) == false )
  178.          return(-1);
  179.    }
  180.  
  181.    kbUnitQueryResetResults(ancestorsQueryID);
  182.    unitCount = kbUnitQueryExecute(ancestorsQueryID);
  183.     return(unitCount);
  184. }
  185.  
  186. //==============================================================================
  187. // initAttack: Creates attack routes, etc.
  188. //==============================================================================
  189. void initAttack(int playerID=-1)
  190. {
  191.    //Destroy all previous attacks (if this isn't the player we're already attacking.
  192.    if (playerID != attackPlayerID)
  193.    {
  194.       //Reset the attack player ID.
  195.       attackPlayerID=-1;
  196.       //Destroy any previous attack plan.
  197.       aiPlanDestroy(attackPlan1ID);
  198.       attackPlan1ID=-1;
  199.       aiPlanDestroy(attackPlan2ID);
  200.       attackPlan2ID=-1;
  201.   
  202.       //Destroy our previous attack paths.
  203.       kbPathDestroy(attackPath1ID);
  204.       attackPath1ID=-1;
  205.       kbPathDestroy(attackPath2ID);
  206.       attackPath2ID=-1;
  207.  
  208.       //Destroy our previous attack routes.
  209.       attackRoute1ID=-1;
  210.       attackRoute2ID=-1;
  211.  
  212.       //Reset the number of attacks.
  213.       numberAttacks=0;
  214.    }
  215.  
  216.    //Save the player to attack.
  217.    attackPlayerID=playerID;
  218.  
  219.    vector gatherPoint1=kbGetBlockPosition("3073");
  220.     vector gatherPoint2=kbGetBlockPosition("3074");
  221.        
  222.     //Setup attack path 1 - go left
  223.    attackPath1ID=kbPathCreate("Attack Path 1");
  224.    kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3075"));
  225.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3076"));
  226.     
  227.    //Create attack route 1.
  228.    attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPoint1, kbGetBlockPosition("3077"));
  229.    
  230.     if (attackRoute1ID >= 0)
  231.       kbAttackRouteAddPath(attackRoute1ID, attackPath1ID);
  232.  
  233.    //Setup attack path 2 - go right
  234.    attackPath2ID=kbPathCreate("Attack Path 2");
  235.    kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3078"));
  236.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3079"));
  237.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3080"));
  238.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3081"));
  239.    //Create attack route 2.
  240.    attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPoint2, kbGetBlockPosition("3082"));
  241.    
  242.     if (attackRoute2ID >= 0)
  243.       kbAttackRouteAddPath(attackRoute2ID, attackPath2ID);
  244. }
  245.  
  246. //==============================================================================
  247. // setupAttack1 (primarily spearmen)
  248. //==============================================================================
  249. bool setupAttack1(int playerID=-1)
  250. {
  251.     int randomPath=aiRandInt(2);
  252.     int randomUnits=aiRandInt(4);
  253.    
  254.     //Info.
  255.     aiEcho("Attacking Player "+playerID+".");
  256.  
  257.    //If the player to attack doesn't match, init the attack.
  258.    if (attackPlayerID != playerID)
  259.    {
  260.       initAttack(playerID);
  261.       if (attackPlayerID < 0)
  262.          return(false);
  263.    }
  264.  
  265.    //Create an attack plan.
  266.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  267.    if (newAttackPlanID < 0)
  268.       return(false);
  269.  
  270.    //Target player (required).  This must work.
  271.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  272.       return(false);
  273.  
  274.    //Gather point.
  275.     vector gatherPoint=kbGetBlockPosition("3073");
  276.  
  277.     //Set the target type.  This must work.
  278.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  279.       return(false);
  280.  
  281.    //Unit types to attack.
  282.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding);
  283.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit);
  284.  
  285.    //Attack route.
  286.    if (randomPath == 0)
  287.     {
  288.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  289.         aiEcho("Attack going left.");
  290.     }
  291.    else
  292.     {
  293.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID);
  294.         aiEcho("Attack going right.");
  295.     }
  296.  
  297.    //Set the gather point and gather point distance.
  298.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  299.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 20.0);
  300.  
  301.    //Set up the attack route usage pattern.
  302.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  303.    
  304.     //Add the primary unit type to the plan.
  305.    aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  306.  
  307.     // Add a sphinx usually, but only if we're going right.
  308.     if ( randomUnits < 3 && randomPath == 1 )
  309.     {
  310.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 1, 1);
  311.     }
  312.  
  313.     // Randomly add two slingers, if available (rare, and only early).
  314.     if ( randomUnits == 0 )
  315.     {
  316.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, 0, 2, 2);
  317.     }
  318.  
  319.     // If we're making Age 3 units, we don't need the priests for the other attack, so use a couple of them here.
  320.     // Also add a siege tower if available.
  321.     if ( makingAge3Units == true )
  322.     {
  323.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 0, 2, 2);
  324.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 1, 3);
  325.  
  326.         // Randomly add two chariot archers, if available.
  327.         if ( randomUnits == 0 )
  328.         {
  329.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 2, 2);
  330.         }
  331.     }
  332.  
  333.    //Set the initial position.
  334.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  335.    //Plan requires all need units to work (can be false).
  336.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  337.    //Activate the plan.
  338.    aiPlanSetActive(newAttackPlanID);
  339.  
  340.    //Now, save the attack plan ID appropriately.
  341.    aiPlanSetOrphan(attackPlan1ID, true);
  342.    attackPlan1ID=newAttackPlanID;
  343.  
  344.    //Increment our overall number of attacks.
  345.    numberAttacks++;
  346. }
  347.  
  348.  
  349. //==============================================================================
  350. // setupAttack2
  351. //==============================================================================
  352. bool setupAttack2(int playerID=-1)
  353. {
  354.     int randomPath=aiRandInt(2);
  355.     int randomUnits=aiRandInt(4);
  356.    
  357.     //Info.
  358.     aiEcho("Attacking Player "+playerID+".");
  359.  
  360.    //If the player to attack doesn't match, init the attack.
  361.    if (attackPlayerID != playerID)
  362.    {
  363.       initAttack(playerID);
  364.       if (attackPlayerID < 0)
  365.          return(false);
  366.    }
  367.  
  368.    //Create an attack plan.
  369.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  370.    if (newAttackPlanID < 0)
  371.       return(false);
  372.  
  373.    //Target player (required).  This must work.
  374.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  375.       return(false);
  376.  
  377.    //Gather point.
  378.     vector gatherPoint=kbGetBlockPosition("3074");
  379.  
  380.     //Set the target type.  This must work.
  381.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  382.       return(false);
  383.  
  384.    //Unit types to attack.
  385.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  386.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  387.  
  388.    //Attack route.
  389.    if (randomPath == 0)
  390.     {
  391.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  392.         aiEcho("Attack going left.");
  393.     }
  394.    else
  395.     {
  396.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID);
  397.         aiEcho("Attack going right.");
  398.     }
  399.  
  400.    //Set the gather point and gather point distance.
  401.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  402.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 20.0);
  403.  
  404.    //Set up the attack route usage pattern.
  405.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  406.    
  407.     //Add the unit types to the plan - which main type depends on what we're maintaining.
  408.     if ( makingAge3Units == true )
  409.     {
  410.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  411.         
  412.         // Randomly throw two to four camels in for fun 40% of the time.  Don't go if you don't have at least two.
  413.         if ( randomUnits < 2 )
  414.         {
  415.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 2, 2, 4);
  416.         }
  417.         
  418.         // If no camels, then sphinxes are likely, but only if we're going right.
  419.         if ( randomUnits < 3 && randomPath == 1 )
  420.         {
  421.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 4, 4);
  422.         }
  423.  
  424.         // Siege towers.  If available.
  425.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 4, 4);
  426.     }
  427.     else
  428.     {
  429.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  430.         
  431.         // Add two spearmen, if available, half the time.
  432.         if ( randomUnits < 2 )
  433.         {
  434.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, 0, 2, 2);
  435.         }
  436.  
  437.         // Possibly one priest; if not, then a siege tower (or two).
  438.         if ( randomUnits > 1 )
  439.         {
  440.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 0, 1, 1);
  441.         }
  442.         else
  443.         {
  444.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 4, 4);
  445.         }
  446.  
  447.         // Rarely a Sphinx, but only if we're going right.
  448.         if ( randomUnits == 1 && randomPath == 1)
  449.         {
  450.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 1, 2);
  451.         }
  452.     }
  453.  
  454.    //Set the initial position.
  455.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  456.    //Plan requires all need units to work (can be false).
  457.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  458.    //Activate the plan.
  459.    aiPlanSetActive(newAttackPlanID);
  460.  
  461.    //Now, save the attack plan ID appropriately.
  462.    aiPlanSetOrphan(attackPlan2ID, true);
  463.    attackPlan1ID=newAttackPlanID;
  464.  
  465.    //Increment our overall number of attacks.
  466.    numberAttacks++;
  467. }
  468.  
  469. //==============================================================================
  470. // Age 2 Tech Research Rules - medium slingers, spearmen, and criosphinx
  471. //==============================================================================
  472. rule researchMediumSlingers
  473.    minInterval 240
  474.    active
  475. {
  476.    int planID=aiPlanCreate("Medium Spearman research", cPlanResearch);
  477.    if (planID < 0)
  478.       return;
  479.  
  480.    aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumSlingers);
  481.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  482.    aiPlanSetActive(planID);
  483.    
  484.     //Done.
  485.    xsDisableSelf();
  486. }
  487.  
  488.  
  489. rule researchMediumSpearmen
  490.    minInterval 300
  491.    active
  492. {
  493.    int planID=aiPlanCreate("Medium Spearman research", cPlanResearch);
  494.    if (planID < 0)
  495.       return;
  496.  
  497.    aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumSpearmen);
  498.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  499.    aiPlanSetActive(planID);
  500.    
  501.     //Done.
  502.    xsDisableSelf();
  503. }
  504.  
  505. rule researchCriosphinx
  506.    minInterval 600
  507.    active
  508. {
  509.    int planID=aiPlanCreate("Criosphinx research", cPlanResearch);
  510.    if (planID < 0)
  511.       return;
  512.  
  513.    aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCriosphinx);
  514.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeTemple);
  515.    aiPlanSetActive(planID);
  516.    
  517.     //Done.
  518.    xsDisableSelf();
  519. }
  520.  
  521.  
  522. //==============================================================================
  523. // Age 3 Tech Research Rules - activated by the Age 3 advancement thing
  524. //==============================================================================
  525. rule researchHeavySpearmen
  526.    minInterval 120
  527.    inactive
  528. {
  529.    int planID=aiPlanCreate("Heavy Spearman research", cPlanResearch);
  530.    if (planID < 0)
  531.       return;
  532.  
  533.    aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavySpearmen);
  534.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  535.    aiPlanSetActive(planID);
  536.    
  537.     //Done.
  538.    xsDisableSelf();
  539. }
  540.  
  541. rule researchBronzeShields
  542.    minInterval 180
  543.    inactive
  544. {
  545.    int planID=aiPlanCreate("Bronze Shields research", cPlanResearch);
  546.    if (planID < 0)
  547.       return;
  548.  
  549.    aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeShields);
  550.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  551.    aiPlanSetActive(planID);
  552.    
  553.     //Done.
  554.    xsDisableSelf();
  555. }
  556.  
  557. rule researchBronzeWeapons
  558.    minInterval 240
  559.    inactive
  560. {
  561.    int planID=aiPlanCreate("Bronze Weapons research", cPlanResearch);
  562.    if (planID < 0)
  563.       return;
  564.  
  565.    aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeWeapons);
  566.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  567.    aiPlanSetActive(planID);
  568.    
  569.     //Done.
  570.    xsDisableSelf();
  571. }
  572.  
  573. rule researchHeavyChariots
  574.    minInterval 240
  575.    inactive
  576. {
  577.    int planID=aiPlanCreate("Heavy Chariot research", cPlanResearch);
  578.    if (planID < 0)
  579.       return;
  580.  
  581.    aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyChariots);
  582.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeMigdolStronghold);
  583.    aiPlanSetActive(planID);
  584.    
  585.     //Done.
  586.    xsDisableSelf();
  587. }
  588.  
  589. rule researchBronzeMail
  590.    minInterval 360
  591.    inactive
  592. {
  593.    int planID=aiPlanCreate("Bronze Mail research", cPlanResearch);
  594.    if (planID < 0)
  595.       return;
  596.  
  597.    aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeMail);
  598.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  599.    aiPlanSetActive(planID);
  600.    
  601.     //Done.
  602.    xsDisableSelf();
  603. }
  604.  
  605. //==============================================================================
  606. // Attack Generator 1 - First Attack Plan
  607. //==============================================================================
  608. rule attackGenerator1
  609.    minInterval 90
  610.    inactive
  611.    group AttackRules
  612. {
  613.    //See how many "idle" attack plans we have.  Don't create any more if we have
  614.    //idle plans.
  615.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  616.  
  617.    if (numberIdleAttackPlans > 0)
  618.       return;
  619.  
  620.    //If we have enough unassigned military units, create a new attack plan.
  621.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID1);
  622.    aiEcho("There are "+numberAvailableUnits+" spearmen available for a new attack.");
  623.    
  624.     if (numberAvailableUnits >= attackMinimumGroupSize)
  625.         setupAttack1(1);
  626. }
  627.  
  628. //==============================================================================
  629. // Attack Generator 2 - Second Attack Plan
  630. //==============================================================================
  631. rule attackGenerator2
  632.    minInterval 105
  633.    inactive
  634.    group AttackRules
  635. {
  636.    //See how many "idle" attack plans we have.  Don't create any more if we have
  637.    //idle plans.
  638.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  639.  
  640.    if (numberIdleAttackPlans > 0)
  641.       return;
  642.  
  643.    //If we have enough unassigned military units of the primary type, create a new attack plan.
  644.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID2);
  645.     if ( makingAge3Units == true )
  646.     {
  647.         numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID6);
  648.     }
  649.  
  650.    aiEcho("There are "+numberAvailableUnits+" slingers/chariot archers available for a new attack.");
  651.    
  652.     if (numberAvailableUnits >= attackMinimumGroupSize)
  653.         setupAttack2(1);
  654. }
  655.  
  656. //==============================================================================
  657. // Attack enablers - enable attacks once timers expire
  658. //==============================================================================
  659. rule attack1Enabler
  660.    minInterval 90
  661.    active
  662.    group AttackRules
  663. {
  664.    xsEnableRule("attackGenerator1");
  665.    xsDisableSelf();
  666. }
  667.  
  668. rule attack2Enabler
  669.     minInterval 120
  670.     active
  671.     group AttackRules
  672. {
  673.     xsEnableRule("attackGenerator2");
  674.     xsDisableSelf();
  675. }
  676.  
  677. //=====================================================================================
  678. // 90 seconds after starting to research Age 3, set things up.
  679. // ...start maintaining anubites and camelry.
  680. // ...and switch to Chariot Archers instead of slingers.
  681. // ...and activate a bunch of other rules, researches, etc.
  682. //=====================================================================================
  683. rule age3Setup
  684.     minInterval 90
  685.     inactive
  686.     group AttackRules
  687. {
  688.     difflevel=aiGetWorldDifficulty();
  689.  
  690.     int age3Active = kbGetTechStatus( cTechAge3Nephthys );
  691.     int camelryMaintained = 4;
  692.  
  693.    //Share the number to maintain.
  694.    int numberToMaintain=attackMinimumGroupSize*2;
  695.  
  696.     vector gatherPointLeft=kbGetBlockPosition("3073");
  697.     vector gatherPointRight=kbGetBlockPosition("3074");
  698.  
  699.    // Verify we're actually where we need to be, then maintain away.
  700.     if (age3Active == cTechStatusActive )
  701.     {
  702.         int maintainPlan1ID=aiPlanCreate("Maintain "+camelryMaintained+" "+kbGetProtoUnitName(attackerUnitTypeID5), cPlanTrain);
  703.         if (maintainPlan1ID >= 0)
  704.         {
  705.             //Must set the type of unit to train.
  706.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID5 );
  707.             //Set the number of units to maintain in the world at one time.
  708.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, camelryMaintained);
  709.             //Don't train units faster than every 30 seconds
  710.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 30);
  711.             //Set a gather point.
  712.             aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointRight);
  713.             //Activate the plan.
  714.             aiPlanSetActive(maintainPlan1ID);
  715.         }
  716.  
  717.         // For missile units, destroy the original plan and make a new one.
  718.         aiPlanDestroy( missileMaintainPlan );
  719.  
  720.         // Chariot archers, baby!
  721.         int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID6), cPlanTrain);
  722.         if (maintainPlan2ID >= 0)
  723.         {
  724.             //Must set the type of unit to train.
  725.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID6);
  726.             //Set the number of units to maintain in the world at one time.
  727.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  728.             //Don't train units faster than every 35 seconds
  729.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 35);
  730.             //Set a gather point.
  731.             aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPointRight);
  732.             //Activate the plan.
  733.             aiPlanSetActive(maintainPlan2ID);
  734.         }
  735.         
  736.         // Set a shared boolean so we know to add fun stuff to our attack groups.
  737.         makingAge3Units = true;
  738.  
  739.         // Enable a bunch of research rules on everything other than Easy.
  740.         if ( difflevel > 0 )
  741.         {
  742.             xsEnableRule("researchHeavySpearmen");
  743.             xsEnableRule("researchHeavyChariots");
  744.             xsEnableRule("researchBronzeShields");
  745.             xsEnableRule("researchBronzeWeapons");
  746.             xsEnableRule("researchBronzeMail");
  747.         }
  748.  
  749.         aiEcho("Now maintaining Age 3 units.");
  750.  
  751.         //Done, disable.
  752.         xsDisableSelf();    
  753.     }
  754. }
  755.  
  756. //================================================================================================
  757. // Advance to Age 3 ten minutes into the game.  This in turn fires off tons o' stuff.
  758. //================================================================================================
  759. rule researchAge3
  760.     minInterval 600
  761.     inactive
  762. {
  763.     int planID=aiPlanCreate("Advancing to Age 3 (Nephthys)", cPlanResearch);
  764.    
  765.     if (planID < 0)
  766.         return;
  767.     
  768.     aiEcho("*** ADVANCING TO AGE 3 ***");
  769.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechAge3Nephthys);
  770.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeCitadelCenter);
  771.     aiPlanSetActive(planID);
  772.     
  773.     // Now maintain Age 3 stuff.
  774.     xsEnableRule("age3Setup");
  775.  
  776.     // Done.
  777.     xsDisableSelf();
  778. }
  779.  
  780. //==============================================================================
  781. // GOD POWER USAGE
  782. //==============================================================================
  783. rule fireEclipse
  784.     minInterval 15
  785.     active
  786.     group AttackRules
  787. {
  788.     int numAttackers=-1;
  789.     vector eclipsePoint=kbGetBlockPosition("3074");
  790.     numAttackers=checkForEclipse();
  791.  
  792.     if (numAttackers > 4)
  793.     {
  794.         aiEcho("Close to enemy town with group, fire Eclipse.");
  795.         if ( aiCastGodPowerAtPosition(cTechEclipse, eclipsePoint) == true )
  796.             xsDisableSelf();
  797.         else
  798.             aiEcho("Eclipse failed - try again later.");
  799.     }
  800. }
  801.  
  802. rule fireAncestors
  803.     minInterval 10
  804.     active
  805.     group AttackRules
  806. {
  807.     int numAttackers=-1;
  808.     vector ancestorsPoint=kbGetBlockPosition("3073");
  809.     numAttackers=checkForAncestors();
  810.  
  811.     if (numAttackers > 4)
  812.     {
  813.         aiEcho("Enemy units coming in, fire Ancestors.");
  814.         if ( aiCastGodPowerAtPosition(cTechSkeletonPower, ancestorsPoint) == true )
  815.             xsDisableSelf();
  816.         else
  817.             aiEcho("Ancestors failed - try again later.");
  818.     }
  819. }
  820.  
  821. //==============================================================================
  822. // MAIN.
  823. //==============================================================================
  824. void main(void)
  825. {
  826.     difflevel=aiGetWorldDifficulty();
  827.  
  828.    //Startup.
  829.    miscStartup();
  830.     int sphinxesMaintained = 4;
  831.  
  832.     // Difficulty Level Adjustments
  833.     if ( difflevel == 0 )
  834.     {
  835.         xsSetRuleMinInterval( "attack1Enabler", 210 );
  836.         xsSetRuleMinInterval( "attack2Enabler", 240 );
  837.         xsSetRuleMinInterval( "attackGenerator2", 150 );
  838.     }
  839.  
  840.     if ( difflevel == 2 )
  841.     {
  842.         sphinxesMaintained = 6;
  843.         attackMinimumGroupSize=7;
  844.         attackMaximumGroupSize=9;
  845.         xsSetRuleMinInterval( "researchAge3", 120 );
  846.     }
  847.     else if ( difflevel == 3 )
  848.     {
  849.         sphinxesMaintained = 8;
  850.         attackMinimumGroupSize=9;
  851.         attackMaximumGroupSize=12;
  852.         xsSetRuleMinInterval( "researchAge3", 10 );
  853.     }
  854.     xsEnableRule("researchAge3");
  855.  
  856.    //Share the number to maintain.
  857.    int numberToMaintain=attackMinimumGroupSize*2;
  858.  
  859.    //Two possible gather points.
  860.    vector gatherPointLeft=kbGetBlockPosition("3073");
  861.     vector gatherPointRight=kbGetBlockPosition("3074");
  862.     vector gatherPointSphinx=kbGetBlockPosition("3079");
  863.  
  864.     vector gatherPointTowers=kbGetBlockPosition("2380");
  865.  
  866.    //Create a simple plan to maintain X spearmen.
  867.    int maintainPlan1ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  868.    if (maintainPlan1ID >= 0)
  869.    {
  870.         //Must set the type of unit to train.
  871.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1);
  872.       //Set the number of units to maintain in the world at one time.
  873.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  874.       //Don't train units faster than every 25 seconds
  875.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 25);
  876.       //Set a gather point.
  877.       aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointLeft);
  878.       //Activate the plan.
  879.       aiPlanSetActive(maintainPlan1ID);
  880.    }
  881.  
  882.     //Create a simple plan to maintain X slingers.
  883.    int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain);
  884.     if (maintainPlan2ID >= 0)
  885.    {
  886.         //Must set the type of unit to train.
  887.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2);
  888.       //Set the number of units to maintain in the world at one time.
  889.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  890.       //Don't train units faster than every 35 seconds
  891.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 35);
  892.       //Set a gather point.
  893.       aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPointRight);
  894.       //Activate the plan.
  895.       aiPlanSetActive(maintainPlan2ID);
  896.  
  897.         //Store off the plan in a variable so we can modify this sucker later.
  898.         //DAL - I wonder if this will work...
  899.         missileMaintainPlan=maintainPlan2ID;
  900.    }
  901.  
  902.     //Create a simple plan to maintain 3 priests.
  903.    int maintainPlan3ID=aiPlanCreate("Maintain 3 "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain);
  904.    if (maintainPlan3ID >= 0)
  905.    {
  906.         //Must set the type of unit to train.
  907.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID3);
  908.       //You can limit the number of units that are ever trained by this plan with this call.
  909.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  910.       //Set the number of units to maintain in the world at one time.
  911.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, 3);
  912.       //Don't train units faster than every 75 seconds
  913.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 75);
  914.       //Set a gather point.
  915.       aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPointRight);
  916.       //Activate the plan.
  917.       aiPlanSetActive(maintainPlan3ID);
  918.    }
  919.  
  920.     // And sphinxes.  Sphinxes are cool.  They don't go out to join attacks very often, though.
  921.     int maintainPlan4ID=aiPlanCreate("Maintain "+sphinxesMaintained+" "+kbGetProtoUnitName(attackerUnitTypeID4), cPlanTrain);
  922.     if (maintainPlan4ID >= 0)
  923.     {
  924.         //Must set the type of unit to train.
  925.         aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanUnitType, 0, attackerUnitTypeID4);
  926.         //Makes total of fifteen, then stops.  Player deserves a break.
  927.         aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToTrain, 0, 15);
  928.         //Set the number of units to maintain in the world at one time.
  929.         aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, sphinxesMaintained);
  930.         //Don't train units faster than every three minutes
  931.         aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 180);
  932.         if ( difflevel > 1 )
  933.         {
  934.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 90);
  935.         }
  936.         //Set a gather point.
  937.         aiPlanSetVariableVector(maintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPointSphinx);
  938.         //Activate the plan.
  939.         aiPlanSetActive(maintainPlan4ID);
  940.     }
  941.  
  942.     // One more.  Keep a siege tower handy at all times.  More on Hard and Titan.
  943.     int maintainPlan5ID=aiPlanCreate("Maintain 1 "+kbGetProtoUnitName(attackerUnitTypeID7), cPlanTrain);
  944.     if (maintainPlan5ID >= 0)
  945.     {
  946.         //Must set the type of unit to train.
  947.         aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanUnitType, 0, attackerUnitTypeID7);
  948.         //Makes total of twenty, then stops.  Player deserves a break.
  949.         aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanNumberToTrain, 0, 20);
  950.         //Set the number of units to maintain in the world at one time.
  951.         aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanNumberToMaintain, 0, 1);
  952.         //Don't train units faster than every 60 seconds
  953.         aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanFrequency, 0, 60);
  954.         if ( difflevel == 2 )
  955.         {
  956.             aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanNumberToMaintain, 0, 3);
  957.             aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanFrequency, 0, 20);
  958.         }
  959.  
  960.         if ( difflevel == 3 )
  961.         {
  962.             aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanNumberToMaintain, 0, 5);
  963.             aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanFrequency, 0, 10);
  964.         }
  965.  
  966.         //Set a gather point.
  967.         aiPlanSetVariableVector(maintainPlan5ID, cTrainPlanGatherPoint, 0, gatherPointTowers);
  968.         //Activate the plan.
  969.         aiPlanSetActive(maintainPlan5ID);
  970.     }
  971.  
  972. }
  973.